import Designer from '@/index';
import Stage from '.';
import { draw, getBezierCurveControlPoints, getBezierCurvePoints, vectorRotation } from '@/utils';
import { computeScrollbar } from './handle';

/**
 * 绘制画布轮廓
 * @this {Stage}
 */
export function drawOutline() {
  const { devMode } = this;
  if (!devMode || devMode.outline === false) return;
  const outline = { showReal: true, showGrid: true, showView: true, ...devMode.outline };
  const { ctx, scale, calc, minEnclosingRect, viewport } = this;
  const c = calc.bind(this);
  // 画布网格坐标轮廓
  if (outline.showGrid) {
    const grid = {
      text: '画布网格坐标轮廓',
      width: this.width,
      height: this.height,
      color: 'green',
    };
    draw.text(ctx, { ...grid, fontSize: c(12), transformConfig: { translateY: c(7) } });
    draw.rect(ctx, { ...grid, strokeStyle: grid.color, lineWidth: c(1) });
  }
  // 画布实际绘制区域轮廓
  if (outline.showReal && minEnclosingRect) {
    const real = {
      text: '画布实际绘制区域轮廓',
      ...minEnclosingRect,
      x: minEnclosingRect.left,
      y: minEnclosingRect.top,
      color: 'blue',
    };
    draw.text(ctx, { ...real, fontSize: c(12), transformConfig: { translateY: c(7) } });
    draw.rect(ctx, { ...real, strokeStyle: real.color, lineWidth: c(1) });
  }
  // 画布视口轮廓
  if (outline.showView) {
    const view = {
      text: '画布视口轮廓',
      ...this.getGridCoord(viewport),
      width: viewport.width / scale,
      height: viewport.height / scale,
      color: 'red',
    };
    draw.text(ctx, { ...view, fontSize: c(12), transformConfig: { translateY: c(7) } });
    draw.rect(ctx, { ...view, strokeStyle: view.color, lineWidth: c(1) });
  }
}

/**
 * 绘制画布背景
 * @this {Stage}
 */
export function drawBg() {
  const { ctx, scale, grid, canvasWidth, canvasHeight } = this;
  if (!grid) return;
  const originCoord = this.getGridCoord({ x: 0, y: 0 }); // 画布左上角网格坐标
  if (Stage.gridPattern && scale > 0.4) {
    ctx.save();
    ctx.fillStyle = ctx.createPattern(Stage.gridPattern, 'repeat');
    ctx.fillRect(originCoord.x, originCoord.y, canvasWidth / scale, canvasHeight / scale);
    ctx.restore();
  }
  // ctx.fillStyle = 'black';
  // ctx.fillRect(originCoord.x, originCoord.y, canvasWidth / scale, canvasHeight / scale);
}

/**
 * 绘制滚动条
 * @this {Stage}
 */
export function drawScrollbar() {
  if (!this.scrollbar) return;
  const { ctx, hoveringScrollbar, scrolling } = this;
  const strokeStyle = 'white'; // 描边颜色
  const fillStyle = 'black'; // 填充颜色
  const strokeOpacity = 0.63; // 描边不透明度
  const fillOpacity = 0.28; // 填充不透明度
  const ratio = 1.4; // hover 不透明度比率
  const { horizontal, vertical } = computeScrollbar.call(this); // 计算后的滚动条
  // 水平滚动条绘制信息
  const horizontalInfo = { scrollbar: horizontal, opacityRatio: hoveringScrollbar?.name === 'horizontal' ? ratio : 1 };
  // 垂直滚动条绘制信息
  const verticalInfo = { scrollbar: vertical, opacityRatio: hoveringScrollbar?.name === 'vertical' ? ratio : 1 };
  // 滚动条操作状态
  if (scrolling) {
    // 当前操作的滚动条
    const { scrollbar } = this.scrollAnchor;
    if (scrollbar?.name === 'horizontal') {
      horizontalInfo.scrollbar = scrollbar;
      horizontalInfo.opacityRatio = ratio;
    }
    if (scrollbar?.name === 'vertical') {
      verticalInfo.scrollbar = scrollbar;
      verticalInfo.opacityRatio = ratio;
    }
  }
  // 绘制水平滚动条
  if (horizontalInfo.scrollbar?.length) {
    const { scrollbar, opacityRatio } = horizontalInfo;
    draw.roundRect(ctx, { ...scrollbar, color: strokeStyle, opacity: strokeOpacity * opacityRatio, isFill: false });
    draw.roundRect(ctx, { ...scrollbar, color: fillStyle, opacity: fillOpacity * opacityRatio });
  }
  // 绘制垂直滚动条
  if (verticalInfo.scrollbar?.length) {
    const { scrollbar, opacityRatio } = verticalInfo;
    draw.roundRect(ctx, { ...scrollbar, color: strokeStyle, opacity: strokeOpacity * opacityRatio, isFill: false });
    draw.roundRect(ctx, { ...scrollbar, color: fillStyle, opacity: fillOpacity * opacityRatio });
  }
}

/**
 * 绘制辅助线
 * @this {Stage}
 */
export function drawGuides() {
  const { ctx, calc, guides } = this;
  const c = calc.bind(this);
  const { guidesColor } = Designer;
  const guideType = (t) => guides.filter(({ type }) => type === t);
  // 绘制辅助线节点轮廓
  guideType('node').forEach(({ node }) =>
    node.drawOutline(ctx, { strokeStyle: guidesColor, lineDash: [], guideOutline: true }),
  );
  // 绘制辅助线
  guideType('line').forEach(({ o, d }) =>
    draw.line(ctx, { o, d, strokeStyle: guidesColor, lineWidth: c(1), lineDash: [c(3), c(2)] }),
  );
  // 绘制距离标签
  guideType('label').forEach(({ x, y, dist }) => {
    const text = `${dist}`;
    const width = c(text.length * 6 + 12);
    const height = c(24);
    const finalX = x - width / 2;
    const finalY = y - height / 2;
    draw.roundRect(ctx, { x: finalX, y: finalY, width, height: c(24), color: guidesColor, radius: c(3) });
    const textX = finalX + width / 2;
    const textY = finalY + height / 2;
    draw.text(ctx, { text, x: textX, y: textY, color: 'white', fontSize: c(12), align: 'center' });
  });
}

/**
 * 绘制节点连线
 * @this {Stage}
 */
export function drawEdges() {
  const { ctx, _edges, edging } = this;
  ctx.save();
  ctx.lineCap = 'round';
  const time = Math.floor(this.frame.time); // 动画执行的时间 ms
  const antDashOffset = -time / 20; // 蚂蚁虚线效果偏移量
  const units = 20; // 路径点选取单位
  const blinkTime = 1000; // 闪烁周期时间
  // 绘制已有节点连线
  _edges.forEach((edge) => {
    if (!edge.visible) return;
    const { type, path, color, lineWidth, lineDash, effect, selected, hovering, effecting, controlPoints } = edge;
    // 计算样式
    const style = {
      color: selected || hovering || effecting ? Designer.theme : color,
      lineDash,
      lineDashOffset: 0, // 虚线偏移量 实现蚂蚁线效果
      opacity: 1, // 不透明度 实现闪烁效果
    };
    let arrowsPoints = []; // 箭头绘制点
    // 节点连线受影响状态
    if (effecting && effect) {
      const ant = effect.includes('ant'); // 蚂蚁爬行效果
      const dashed = effect.includes('dashed'); // 虚线效果
      const arrows = effect.includes('arrows'); // 箭头效果
      const blink = effect.includes('blink'); // 闪烁效果
      // 虚线效果
      if (dashed) {
        style.lineDash = [4];
        style.lineDashOffset = ant ? antDashOffset : 0;
      }
      // 箭头效果
      if (arrows) {
        // 路径选取下标
        const selectIndex = Math.floor(ant ? time / units : units / 2) % units;
        // 贝塞尔曲线
        if (type === 'bezier') {
          arrowsPoints = getBezierCurvePoints(controlPoints, 0.5)
            .filter((point, index, array) => {
              point.next = array[index + 1];
              return point.next && index % units === selectIndex;
            })
            .map((point) => {
              const rotation = vectorRotation(point, point.next) + 90;
              return { point, rotation };
            });
        }
      }
      // 闪烁效果
      if (blink) {
        let opacity = (time % blinkTime) / (blinkTime / 2); // 循环 0~2
        if (opacity >= 1) opacity = 2 - opacity; // 循环 0~1~0
        style.opacity = opacity;
      }
    }
    ctx.beginPath();
    ctx.strokeStyle = style.color;
    ctx.lineWidth = lineWidth;
    ctx.setLineDash(style.lineDash);
    ctx.lineDashOffset = style.lineDashOffset;
    ctx.globalAlpha = style.opacity;
    ctx.stroke(path);
    // 绘制路径箭头
    arrowsPoints.forEach(({ point, rotation }) => {
      draw.arrows(ctx, { ...point, base: 8, height: 8, color: style.color, transformConfig: { rotation } });
    });
  });
  // 绘制临时节点连线
  if (edging) {
    const { tempAnchor, gridCoord, hoveringAnchor } = this;
    const { type, color, dangerColor, lineWidth } = this.edgeConfig;
    const _sourceAnchor = tempAnchor;
    const _targetAnchor = hoveringAnchor || { p: gridCoord };
    ctx.beginPath();
    ctx.strokeStyle = this.tempEdge?.canAdd === false ? dangerColor : color;
    ctx.lineWidth = lineWidth;
    ctx.setLineDash([4]);
    ctx.lineDashOffset = antDashOffset;
    ctx.globalAlpha = 1;
    // 贝塞尔曲线
    if (type === 'bezier') {
      const [p0, p1, p2, p3] = getBezierCurveControlPoints(_sourceAnchor, _targetAnchor);
      ctx.moveTo(p0.x, p0.y);
      ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
    }
    ctx.stroke();
  }
  ctx.restore();
}
